﻿using Common;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.

// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// ReSharper disable ConditionIsAlwaysTrueOrFalse
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.

// ReSharper disable InconsistentNaming
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

namespace NewDevice
{
    /// <summary>
    /// 老平台的串口通信基类 支持发送/接收 ASCII 字符串和字节流
    /// </summary>
    public class OldSerialBase
    {
        private SerialPort sp; // 串口对象
        private byte[] receivedBytes; // 接收到的字节数据
        private string receivedString; // 接收到的字符串数据
        private bool connected; // 连接状态标志
        private ManualResetEventSlim receivedOkDataEvent = new ManualResetEventSlim(false); // 数据接收完成事件
        private readonly object comLock = new object(); // 数据锁，用于线程同步

        /// <summary>
        /// 设备名称
        /// </summary>
        protected internal string deviceName;

        private const int MaxRetryAttempts = 5; // 最大重试次数
        private bool isAscii; // 标识当前是否为ASCII模式

        private Func<byte[], bool> FuncReceivedDataVerify; // 接收数据验证委托

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="deviceName">设备名称</param>
        public OldSerialBase(string deviceName = "RS485总线")
        {
            this.deviceName = deviceName;
        }

        /// <summary>
        /// 连接串口
        /// </summary>
        /// <param name="port">串口号</param>
        /// <param name="baud">波特率</param>
        /// <param name="dataBits">数据位</param>
        /// <param name="stopBits">停止位</param>
        /// <param name="parity">校验位</param>
        /// <param name="timeout">超时时间(ms)</param>
        public void Connect(string port, int baud = 9600, int dataBits = 8,
            StopBits stopBits = StopBits.One, Parity parity = Parity.None,
            int timeout = 500)
        {
            if (connected)
            {
                MyLog.Debug($"{deviceName} 已经连接，无需重复连接。");
                return;
            }

            // 初始化串口参数
            sp = new SerialPort(port, baud)
            {
                DataBits = dataBits,
                StopBits = stopBits,
                Parity = parity,
                ReadTimeout = timeout,
                WriteTimeout = timeout,
                ReadBufferSize = 1024,
                WriteBufferSize = 1024
            };

            sp.DataReceived += Sp_DataReceived; // 注册数据接收事件

            try
            {
                sp.Open(); // 打开串口
            }
            catch (Exception ex)
            {
                throw new Exception($"{deviceName} 打开串口[{port}]失败: {ex.Message}", ex);
            }

            if (!sp.IsOpen)
            {
                throw new Exception($"{deviceName} 串口[{port}]打开失败");
            }

            connected = true; // 更新连接状态
            MyLog.Debug(message: $"串口[{port}]已连接", deviceName: deviceName);
        }

        /// <summary>
        /// 发送 ASCII 字符串
        /// </summary>
        /// <param name="cmd">要发送的ASCII字符串</param>
        protected internal void SendAsciiString(string cmd)
        {
            RetryAction(() =>
            {
                isAscii = true; // 设置为ASCII模式
                PrepareForReceiving(); // 准备接收数据
                if (!connected || sp == null || !sp.IsOpen)
                {
                    throw new InvalidOperationException($"{deviceName} 串口未连接或未打开");
                }

                MyLog.DeviceData(deviceName: deviceName, sendData: cmd);
                byte[] data = Encoding.ASCII.GetBytes(cmd); // 转换为ASCII字节数组
                sp?.Write(data, 0, data.Length); // 发送数据
            });
        }

        /// <summary>
        /// 发送 ASCII 字符串
        /// </summary>
        /// <param name="cmd">要发送的ASCII字符串</param>
        /// <param name="timeout">超时时间</param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="TimeoutException"></exception>
        protected internal string SendAsciiStringAndWaitResponse(string cmd, int timeout = 500)
        {
            return RetryAction((Func<string>)(() =>
            {
                lock (comLock) // 加锁保证线程安全
                {
                    isAscii = true; // 设置为ASCII模式
                    if (!connected || sp == null || !sp.IsOpen)
                    {
                        throw new InvalidOperationException($"{deviceName} 串口未连接或未打开");
                    }

                    PrepareForReceiving(); // 准备接收数据
                    MyLog.DeviceData(deviceName: deviceName, sendData: cmd);

                    byte[] data = Encoding.ASCII.GetBytes(cmd); // 转换为ASCII字节数组
                    sp?.Write(data, 0, data.Length); // 发送数据

                    if (!receivedOkDataEvent.Wait(timeout)) // 等待数据接收完成
                    {
                        throw new TimeoutException($"{deviceName} 接收字节流超时");
                    }

                    Thread.Sleep(10);
                    return receivedString;
                }
            }));
        }

        /// <summary>
        /// 发送字节数据并等待响应
        /// </summary>
        /// <param name="data">要发送的字节数组</param>
        /// <param name="func">数据验证函数</param>
        /// <param name="timeout">超时时间(ms)</param>
        /// <returns>接收到的字节数组</returns>
        protected internal byte[] SendBytesAndWaitResponse(byte[] data, Func<byte[], bool> func, int timeout = 1000)
        {
            return RetryAction((Func<byte[]>)(() =>
            {
                lock (comLock)
                {
                    isAscii = false; // 设置为字节模式
                    FuncReceivedDataVerify = func; // 设置数据验证函数
                    if (!connected || sp == null || !sp.IsOpen)
                    {
                        return null;
                        throw new InvalidOperationException($"{deviceName} 串口未连接或未打开");
                    }

                    MyLog.DeviceData(deviceName: deviceName, sendData: BitConverter.ToString(data).Replace('-', ' '));
                    PrepareForReceiving(); // 准备接收数据
                    sp?.Write(data, 0, data.Length); // 发送数据

                    if (!receivedOkDataEvent.Wait(timeout)) // 等待数据接收完成
                    {
                        throw new TimeoutException($"{deviceName} 接收字节流超时");
                    }

                    Thread.Sleep(10);
                    return receivedBytes; // 返回接收到的数据
                }
            }));
        }

        /// <summary>
        /// 发送字节数据
        /// </summary>
        /// <param name="data">要发送的字节数组</param>
        protected internal void SendBytes(byte[] data)
        {
            RetryAction(() =>
            {
                isAscii = false; // 设置为字节模式
                PrepareForReceiving(); // 准备接收数据
                if (!connected || sp == null || !sp.IsOpen)
                {
                    throw new InvalidOperationException($"{deviceName} 串口未连接或未打开");
                }

                MyLog.DeviceData(deviceName: deviceName, sendData: BitConverter.ToString(data).Replace("-", " "));

                sp?.Write(data, 0, data.Length); // 发送数据
            });
        }

        // <summary>
        // 接收ASCII字符串
        // </summary>
        // <param name="timeout">超时时间(ms)</param>
        // <returns>接收到的字符串</returns>
        //protected internal string ReceiveAsciiString(int timeout = 500)
        //{
        //    return RetryAction((Func<string>)(() =>
        //    {
        //        if (!receivedOkDataEvent.Wait(timeout))  // 等待数据接收完成
        //        {
        //            throw new TimeoutException($"{deviceName} 接收 ASCII 字符串超时");
        //        }

        //        lock (this.comLock)  // 加锁保证线程安全
        //        {
        //            return receivedString;  // 返回接收到的字符串
        //        }
        //    }));
        //}

        // <summary>
        // 接收字节数据
        // </summary>
        // <param name="timeout">超时时间(ms)</param>
        // <returns>接收到的字节数组</returns>
        //protected internal byte[] ReceiveBytes(int timeout = 500)
        //{
        //    return RetryAction((Func<byte[]>)(() =>
        //    {
        //        if (!receivedOkDataEvent.Wait(timeout))  // 等待数据接收完成
        //        {
        //            throw new TimeoutException($"{deviceName} 接收字节流超时");
        //        }

        //        lock (this.comLock)  // 加锁保证线程安全
        //        {
        //            return receivedBytes;  // 返回接收到的字节数组
        //        }
        //    }));
        //}

        /// <summary>
        /// 准备接收数据（清空缓冲区）
        /// </summary>
        private void PrepareForReceiving()
        {
            //lock (comLock)  // 加锁保证线程安全
            //{
            receivedString = null; // 清空接收字符串
            receivedBytes = null; // 清空接收字节数组
            //}

            receivedOkDataEvent.Reset(); // 重置事件
            sp?.DiscardInBuffer(); // 清空输入缓冲区
            sp?.DiscardOutBuffer(); // 清空输出缓冲区
        }

        /// <summary>
        /// 串口数据接收事件处理
        /// </summary>
        private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                SerialPort port = sender as SerialPort;
                if (port == null) return;

                if (isAscii) // ASCII模式处理
                {
                    receivedString = port.ReadLine(); // 读取一行ASCII字符串
                    MyLog.DeviceData(deviceName: deviceName, receivedData: receivedString);
                    receivedOkDataEvent?.Set(); // 设置接收完成事件
                }
                else // 字节模式处理
                {
                    int bytesToRead = port.BytesToRead; // 获取可读字节数

                    byte[] buffer = new byte[bytesToRead]; // 创建缓冲区

                    port.Read(buffer, 0, bytesToRead); // 读取数据

                    if (buffer != null && buffer.Length > 0)
                    {
                        if (receivedBytes == null)
                        {
                            {
                                receivedBytes = buffer; // 直接赋值
                            }
                        }
                        else
                        {
                            {
                                receivedBytes = receivedBytes.Concat(buffer).ToArray(); // 合并字节数组
                            }
                        }

                        MyLog.DeviceData(deviceName: deviceName,
                            receivedData: BitConverter.ToString(buffer).Replace('-', ' '));

                        if (FuncReceivedDataVerify is null) // 无验证函数直接完成
                        {
                            receivedOkDataEvent?.Set();
                        }
                        else
                        {
                            bool verifyRet = FuncReceivedDataVerify.Invoke(receivedBytes); // 验证数据
                            if (verifyRet)
                                receivedOkDataEvent?.Set(); // 验证通过设置完成事件
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MyLog.Error(new Exception(message: $"{deviceName} 数据接收错误: {ex.Message}")); // 记录错误日志
            }
        }

        /// <summary>
        /// 带重试机制的操作（有返回值）
        /// </summary>
        private T RetryAction<T>(Func<T> action)
        {
            int attempt = 0;
            while (attempt < MaxRetryAttempts)
            {
                try
                {
                    return action(); // 执行操作
                }
                catch (Exception ex)
                {
                    attempt++;
                    MyLog.Error(new Exception($"{deviceName} 第 {attempt} 次尝试失败: {ex.Message},  deviceName:{deviceName}" + "___" + ex.Message
                        )); // 记录重试日志
                    MyLog.Debug(message: $"{deviceName} 第 {attempt} 次尝试失败: {ex.Message}",
                        deviceName: deviceName); // 记录重试日志
                    if (attempt >= MaxRetryAttempts)
                    {
                        throw new Exception($"{deviceName} 通信异常", ex); // 抛出异常
                    }

                    Thread.Sleep(500); // 延迟500ms后重试
                }
            }

            return default(T);
        }

        /// <summary>
        /// 带重试机制的操作（无返回值）
        /// </summary>
        private void RetryAction(Action action)
        {
            int attempt = 0;
            while (attempt < MaxRetryAttempts)
            {
                try
                {
                    action(); // 执行操作
                    return;
                }
                catch (Exception ex)
                {
                    attempt++;
                    MyLog.Debug($"{deviceName} 第 {attempt} 次尝试失败: {ex.Message}"); // 记录重试日志
                    if (attempt >= MaxRetryAttempts)
                    {
                        throw new Exception($"{deviceName} 重试次数已达上限，操作失败", ex); // 抛出异常
                    }

                    Thread.Sleep(500); // 延迟500ms后重试
                }
            }
        }

        /// <summary>
        /// 关闭串口连接
        /// </summary>
        public void Close()
        {
            if (sp != null && sp.IsOpen)
            {
                sp.Close(); // 关闭串口
                connected = false; // 更新连接状态
                MyLog.Debug(deviceName: deviceName, message: $"串口[{sp.PortName}]断开连接");
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            receivedOkDataEvent?.Dispose(); // 释放事件对象

            if (sp != null)
            {
                if (sp.IsOpen)
                {
                    sp.Close(); // 关闭串口
                }

                sp.Dispose(); // 释放串口对象
            }

            connected = false; // 更新连接状态
        }
    }
}